package generic.genetic.chromosome;

import generic.genetic.gene.Gene;

import java.util.Arrays;
import java.util.Random;

public abstract class Chromosome implements Comparable, Cloneable{

	protected double tolerance;
	protected int nGenes;
	protected Gene[] genes;
	
	public abstract float evaluate();
	public abstract Chromosome getCopy();
	
	public Chromosome(){
		nGenes = 0;
	}
	
	public Chromosome(int nGenes){
		this.nGenes = nGenes;
		genes = new Gene[nGenes];
	}
	
	public Chromosome(Chromosome c) {
		this.nGenes = c.nGenes;
		this.genes = new Gene[nGenes];
		for (int i=0;i<genes.length;i++) genes[i] = c.getGenes()[i].getCopy();
	}

	public void randomInit(int type){
		for (int i=0;i<nGenes;i++) genes[i].randomInit();	
	}

	/**
	 * This method crosses the current chromosome with c chromosome in some random point
	 * (or various points if multiGene activated) and modifies previous chromosomes
	 * @param multiGene One cross point per Gene
	 * @param c The chromosome to cross with
	 */
	public void cross(int type, Chromosome c){
		Random r = new Random();
		if (type == 0){
			for (int i=0;i<genes.length;i++){
				Gene currGene = c.getGenes()[i];
				genes[i].cross(currGene, r.nextInt(currGene.getLength()));
			}
		} else {
			int length = 0;
			float[] probs = new float[genes.length];
			for (Gene g : genes) length += g.getLength();
			probs[0] = ((float) genes[0].getLength())/length;
			for (int i=1;i<genes.length;i++) probs[i] = ((float) genes[i].getLength())/length + probs[i-1];
			float r_float = r.nextFloat();
			
			int i=0;			
			while (i<genes.length && r_float > probs[i]) i++;
			if (i == genes.length) i--;
			
			int crossPoint = r.nextInt(genes[i].getLength()-1);
			genes[i].cross(c.getGenes()[i], r.nextInt(length-1));
			
			while (i<genes.length){
				Object tmp = genes[i];
				genes[i] = c.getGenes()[i];
				c.setGenes(i, (Gene) tmp);
				i++;
			}			
		}
	}
	
	private void cross_simple(Chromosome c){
		
	}
	
	private void cross_multigene(Chromosome c){
		
	}
	
	private void setGenes(int i, Gene tmp) {
		genes[i] = tmp;		
	}
	public boolean mutation(int type, double mutation_rate){
		boolean ret = false;
		for (Gene g : genes) ret &= g.mutation(mutation_rate);
		return ret;
	}
	
	public void setnGenes(int nGenes) {
		this.nGenes = nGenes;
	}

	public void setGenes(Gene[] genes) {
		this.genes = genes;
	}

	public int getnGenes() {
		return nGenes;
	}

	public Gene[] getGenes() {
		return genes;
	}

	@Override
	public String toString() {
		return Arrays.toString(genes) + " -> " + evaluate();
	}
	
	public String getString() {
		return Arrays.toString(genes) + " -> " + evaluate();
	}

	
	@Override
	public int compareTo(Object arg0) {
		float cmp = (this.evaluate() - ((Chromosome) arg0).evaluate());
		if (cmp < 0){
			return -1;
		} else if (cmp > 0) {
			return 1;
		} else {
			return 0;
		}
	}
	@Override
	protected Chromosome clone(){
		return getCopy();
	}
}
